/***************************************************************
 *
 * (c) 2008 Markus Dittrich 
 *
 * This program is free software; you can redistribute it 
 * and/or modify it under the terms of the GNU General Public 
 * License Version 3 as published by the Free Software Foundation. 
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License Version 3 for more details.
 *
 * You should have received a copy of the GNU General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, 
 * Boston, MA 02111-1307, USA.
 *
 ****************************************************************/

/* STL includes */
#include <vector>
//#include <iostream>

/* Qt includes */
#include <QCheckBox>
#include <QColorDialog>
#include <QComboBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QPalette>
#include <QString>
#include <QRadioButton>
#include <QSpinBox>
#include <QTableWidgetItem>
#include <QTableWidget>
#include <QVBoxLayout>

/* local includes */
#include "basic_definitions.h"
#include "property_widgets.h"
#include "utilities.h"

/* pull a few symbols into namespace */
using std::vector;
using boost::tuple;


/***************************************************************
 *
 *
 * member functions for BasePropertyWidget
 *
 *
 **************************************************************/

//--------------------------------------------------------------
// constructor
//--------------------------------------------------------------
BasePropertyWidget::BasePropertyWidget(QWidget* theParent) 
  :
    QWidget(theParent)
{
  // nothing to do
}


//--------------------------------------------------------------
// defitinition for virtual constructor
//--------------------------------------------------------------
BasePropertyWidget::~BasePropertyWidget()
{
  // nothing to do
}




/***************************************************************
 *
 *
 * member functions for DataPropertyWidget
 *
 *
 **************************************************************/

/***************************************************************
 *
 * public member functions
 * 
 ***************************************************************/

//----------------------------------------------------------------
// constructor
//----------------------------------------------------------------
DataPropertyWidget::DataPropertyWidget(QWidget* myParent, 
    const QString& dataName, DataObjectPtr theData, 
    PlotPropertiesPtr plotProperties)
  :
    BasePropertyWidget(myParent),
    numDataItemsString_(QString("# data items: ")),
    parent_(myParent),
    dataName_(dataName),
    plotProperties_(plotProperties),
    data_(theData)
{
  status_ = SUCCESSFULLY_CONSTRUCTED;
}


//----------------------------------------------------------------
// main initialization function
//----------------------------------------------------------------
bool DataPropertyWidget::Init()
{
  if ( status_ != SUCCESSFULLY_CONSTRUCTED )
  {
    return false;
  }

  set_penStyle_map_();
  generate_main_layout_();
  generate_num_dataitems_label_();
  generate_plot_property_selectors_();
  generate_xy_data_selectors_();
  generate_property_table_();
  populate_main_layout_();

  /* connection for re-drawing of the parent canvas */
  connect(this, SIGNAL(update_and_rescale_canvas()), parent_, 
      SLOT(redraw_and_rescale_canvas()));
  connect(this, SIGNAL(update_canvas()), parent_,
      SLOT(redraw_canvas()));
  connect(this, SIGNAL(color_changed(const QString&, const QColor&)), 
      this, SIGNAL(update_canvas()));
  connect(this, SIGNAL(color_changed(const QString&, const QColor&)), 
      parent_, SIGNAL(graph_color_changed(const QString&, 
      const QColor&)));

  return true;
}


//----------------------------------------------------------------
// update the GUI with the current value of the data and plot
// properties
//----------------------------------------------------------------
void DataPropertyWidget::update_widget(DataObjectPtr newData)
{
  /* install new data */
  data_ = newData;

  populate_property_table_();
}


//----------------------------------------------------------------
// return name of data set handled by this tab
//----------------------------------------------------------------
const QString& DataPropertyWidget::get_name() const
{
  return dataName_;
}


/*****************************************************************
 *
 * private slots 
 *
 *****************************************************************/

//----------------------------------------------------------------
// open up a QColorDialog and change color to the requested
// value
//----------------------------------------------------------------
void DataPropertyWidget::change_color_()
{
  QColor aColor = QColorDialog::getColor();

  if ( aColor.isValid() && aColor != plotProperties_->get_color() )
  {
    /* update the properties and widgets */
    plotProperties_->set_color(aColor);
    set_widget_back_color_(plotColorChooser_, aColor);

    emit color_changed(dataName_, aColor);
  }
}


//-----------------------------------------------------------------
// change the line style and force the canvas to re-draw
//-----------------------------------------------------------------
void DataPropertyWidget::on_lineStyle_change_(QString newLineStyle)
{
  plotProperties_->set_penStyle(newLineStyle);

  emit update_canvas();
}


//----------------------------------------------------------------
// handle changes in displayed x data set.
//----------------------------------------------------------------
void DataPropertyWidget::x_data_selection_changed_(int xSelection)
{
  data_->set_active_x_column(xSelection);
  populate_property_table_();
  emit update_and_rescale_canvas();
}



//----------------------------------------------------------------
// handle changes in displayed y data set
//----------------------------------------------------------------
void DataPropertyWidget::y_data_selection_changed_(int ySelection)
{
  data_->set_active_y_column(ySelection);
  populate_property_table_();
  emit update_and_rescale_canvas();
}


//----------------------------------------------------------------
// change the line width to the selected one in the 
// plotProperties container
//----------------------------------------------------------------
void DataPropertyWidget::change_line_width_(int newWidth)
{
  plotProperties_->set_line_width(newWidth);
  emit update_canvas();
}



/******************************************************************
 *
 * private member functions
 *
 ******************************************************************/

//-----------------------------------------------------------------
// populate main layout to generate tab
//-----------------------------------------------------------------
void DataPropertyWidget::populate_main_layout_()
{
  mainLayout_->addWidget(numDataItemsLabel_);
  mainLayout_->addWidget(propertyTable_);
  mainLayout_->addLayout(plotPropertiesLayout_);
  mainLayout_->addLayout(xyDataSelectorLayout_);
}


//-----------------------------------------------------------------
// generate main layout for current tab
//-----------------------------------------------------------------
void DataPropertyWidget::generate_main_layout_()
{
  mainLayout_ = new QVBoxLayout(this);
  mainLayout_->setContentsMargins(2,2,2,2);
}


//-----------------------------------------------------------------
// generate label listing the number of data items
//-----------------------------------------------------------------
void DataPropertyWidget::generate_num_dataitems_label_()
{
  QString length;
  length.setNum(data_->get_num_rows());
  QString lengthString(numDataItemsString_ + length);
  numDataItemsLabel_ = new QLabel(lengthString);
}


//-----------------------------------------------------------------
// update the data items label 
//-----------------------------------------------------------------
void DataPropertyWidget::update_num_dataitems_label_()
{
  QString length;
  length.setNum(data_->get_num_rows());
  QString lengthString(numDataItemsString_ + length);
  numDataItemsLabel_->setText(lengthString);
}


//-----------------------------------------------------------------
// generate plot property (color, line type) selector widgets 
//-----------------------------------------------------------------
void DataPropertyWidget::generate_plot_property_selectors_()
{
  plotPropertiesLayout_ = new QGridLayout;
  plotPropertiesLayout_->setVerticalSpacing(1);

  /* add labels for widget */
  QLabel* colorLabel = new QLabel(tr("color"));
  QLabel* lineWidthLabel = new QLabel(tr("line width"));
  QLabel* plotStyleLabel = new QLabel(tr("plot style"));
  plotPropertiesLayout_->addWidget(colorLabel,0,1,Qt::AlignJustify);
  plotPropertiesLayout_->addWidget(
      lineWidthLabel,0,2,Qt::AlignJustify);
  plotPropertiesLayout_->addWidget(
      plotStyleLabel,0,3,Qt::AlignJustify);

  /* add color selector */
  plotColorChooser_ = new QPushButton;
  set_widget_back_color_(plotColorChooser_, 
      plotProperties_->get_color());
  connect(plotColorChooser_,SIGNAL(clicked()),this,
    SLOT(change_color_()));
  plotPropertiesLayout_->addWidget(plotColorChooser_,1,1);

  /* add line width selector; 
   * for now, enable a range from 1 (default) to 10 */
  QComboBox* lineWidthChooser = new QComboBox;
  for(int lineWidth=1; lineWidth <= 10; ++lineWidth)
  {
    lineWidthChooser->addItem(QString::number(lineWidth));
  }

  plotPropertiesLayout_->addWidget(lineWidthChooser,1,2); 
  connect(lineWidthChooser,SIGNAL(currentIndexChanged(int)),
      this,SLOT(change_line_width_(int)));

  /* add plot style selector */
  QComboBox* PlotStyleChooser = new QComboBox;
  FOREACH(QString item, penStyleContainer_)
  {
    PlotStyleChooser->addItem(item);
  }

  QString activeStyle = plotProperties_->get_penStyle();
  int activeIndex = PlotStyleChooser->findText(activeStyle);
  PlotStyleChooser->setCurrentIndex(activeIndex);

  plotPropertiesLayout_->addWidget(PlotStyleChooser,1,3); 
  connect(PlotStyleChooser, SIGNAL(currentIndexChanged(QString)),
    this, SLOT(on_lineStyle_change_(QString)));
}


//-----------------------------------------------------------------
// create selectors for the currently displayed x and y data sets 
//-----------------------------------------------------------------
void DataPropertyWidget::generate_xy_data_selectors_() 
{
  xyDataSelectorLayout_ = new QGridLayout;
  xyDataSelectorLayout_->setVerticalSpacing(1);

  /* add labels for widget */
  QLabel* xDataLabel = new QLabel(tr("x data selector"));
  QLabel* yDataLabel = new QLabel(tr("y data selector"));
  xyDataSelectorLayout_->addWidget(xDataLabel,0,1,Qt::AlignJustify);
  xyDataSelectorLayout_->addWidget(yDataLabel,0,2,Qt::AlignJustify);

  /* add possible selections */
  xDataChooser_ = new QComboBox;
  yDataChooser_ = new QComboBox;
  xDataChooser_->addItem("ELEMENTS");
  yDataChooser_->addItem("ELEMENTS");
  for ( int counter = 0; counter < data_->get_num_columns(); ++counter)
  {
    QString entry = QString("column_%1").arg(counter);
    xDataChooser_->addItem(entry);
    yDataChooser_->addItem(entry);
  }

  /* set to currently active selection;
   * NOTE: since get_active_x/y_column returns -1 for
   * ELEMENT_COUNT and 0,... for columns 0,.. we need to add 1 
   * to select the correct entry */
  int activeXColumn = data_->get_active_x_column();
  int activeYColumn = data_->get_active_y_column();
  xDataChooser_->setCurrentIndex(activeXColumn + 1);
  yDataChooser_->setCurrentIndex(activeYColumn + 1);

  /* connect data choosers to slots handling selection events */
  connect(xDataChooser_, SIGNAL(currentIndexChanged(int)), 
      this, SLOT(x_data_selection_changed_(int)));
  connect(yDataChooser_, SIGNAL(currentIndexChanged(int)), 
      this, SLOT(y_data_selection_changed_(int)));

  /* add to layout */
  xyDataSelectorLayout_->addWidget(xDataChooser_,1,1);
  xyDataSelectorLayout_->addWidget(yDataChooser_,1,2);
}
 


//-----------------------------------------------------------------
// creates the table holding the data properties
//-----------------------------------------------------------------
void DataPropertyWidget::generate_property_table_()
{
  propertyTable_ = new QTableWidget(NUM_ROWS_, NUM_COLUMNS_);
  propertyTable_->setMinimumHeight(PROPERTY_TABLE_HEIGHT_);
  propertyTable_->setMaximumHeight(PROPERTY_TABLE_HEIGHT_);

  /** items should be read-only and not selectable */
  propertyTable_->setSelectionMode(QAbstractItemView::NoSelection);
  propertyTable_->setEditTriggers(QAbstractItemView::NoEditTriggers);

  QStringList headerNames;
  headerNames << "x" << "y";

  QStringList propertyNames;
  propertyNames << "min" << "max" << "mean" << "med" << "std";

  propertyTable_->setHorizontalHeaderLabels(headerNames);
  propertyTable_->setVerticalHeaderLabels(propertyNames);

  /* size rows and columns */
  for ( int column = 0; column < headerNames.size(); ++column)
  {
    propertyTable_->setColumnWidth(column, COLUMN_WIDTH_);
  }

  for ( int row = 0; row < propertyNames.size(); ++row)
  {
    propertyTable_->setRowHeight(row, ROW_HEIGHT_);
  }

  xMinItem_ = new QTableWidgetItem;
  xMinItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(0, 0, xMinItem_);
  
  yMinItem_ = new QTableWidgetItem;
  yMinItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(0, 1, yMinItem_);
  
  xMaxItem_ = new QTableWidgetItem;
  xMaxItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(1, 0, xMaxItem_);

  yMaxItem_ = new QTableWidgetItem;
  yMaxItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(1, 1, yMaxItem_);
  
  xMeanItem_ = new QTableWidgetItem;   
  xMeanItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(2, 0, xMeanItem_);
  
  yMeanItem_ = new QTableWidgetItem;
  yMeanItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(2, 1, yMeanItem_);
  
  xMedianItem_ = new QTableWidgetItem;   
  xMedianItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(3, 0, xMedianItem_);
  
  yMedianItem_ = new QTableWidgetItem;
  yMedianItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(3, 1, yMedianItem_);
  
  xStdItem_ = new QTableWidgetItem;   
  xStdItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(4, 0, xStdItem_);
  
  yStdItem_ = new QTableWidgetItem;
  yStdItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(4, 1, yStdItem_);


  /* populate it with contents */
  populate_property_table_();
}


//---------------------------------------------------------------
// wrapper to populate the property table with actual data values
//---------------------------------------------------------------
void DataPropertyWidget::populate_property_table_()
{
  const vector<double>& xData = data_->get_x_data();
  populate_property_table_with_x_data_(xData);
  
  const vector<double>& yData = data_->get_y_data();
  populate_property_table_with_y_data_(yData);
}


//---------------------------------------------------------------
// adds the properties of the x data
//---------------------------------------------------------------
void DataPropertyWidget::populate_property_table_with_x_data_(
  const vector<double>& aData)
{
  double min = 0.0;
  double max = 0.0;
  double mean = 0.0;
  double median = 0.0;
  double std = 0.0;

  /* if the x-axis displays the element count we need a special
   * treatment for min, max, and friends */
  if ( xDataChooser_->currentIndex() == HAS_ELEMENT_COUNT )
  {
    mean = 0.0;
    max = static_cast<double>(data_->get_num_rows());
    mean = 0.5 * max;
    median = mean;
    std = 0.0;
  }
  else
  {
    data_min(aData.begin(), aData.end(), min);
    data_max(aData.begin(), aData.end(), max);
    data_mean(aData.begin(), aData.end(), mean);
    data_median(aData.begin(), aData.end(), median);
    data_stddev(aData.begin(), aData.end(), std);
  }

  xMinItem_->setText(pretty_format_double(min)); 
  xMaxItem_->setText(pretty_format_double(max));
  xMeanItem_->setText(pretty_format_double(mean));
  xMedianItem_->setText(pretty_format_double(median));
  xStdItem_->setText(pretty_format_double(std));
}


//---------------------------------------------------------------
// adds the properties of the y data
//---------------------------------------------------------------
void DataPropertyWidget::populate_property_table_with_y_data_(
  const vector<double>& aData)
{
  double min = 0.0;
  double max = 0.0;
  double mean = 0.0;
  double median = 0.0;
  double std = 0.0;

  /* if the y-axis displays the element count we need a special
   * treatment for min, max, and friends */
  if ( yDataChooser_->currentIndex() == HAS_ELEMENT_COUNT )
  {
    min = 0.0;
    max = static_cast<double>(data_->get_num_rows());
    mean = 0.5 * max;
    median = mean;
    std = 0.0;
  }
  else
  {
    data_min(aData.begin(), aData.end(), min);
    data_max(aData.begin(), aData.end(), max);
    data_mean(aData.begin(), aData.end(), mean);
    data_median(aData.begin(), aData.end(), median);
    data_stddev(aData.begin(), aData.end(), std);
  }

  yMinItem_->setText(pretty_format_double(min)); 
  yMaxItem_->setText(pretty_format_double(max));
  yMeanItem_->setText(pretty_format_double(mean));
  yMedianItem_->setText(pretty_format_double(median));
  yStdItem_->setText(pretty_format_double(std));
}


//--------------------------------------------------------------
// populate the map matching QString descriptions with the 
// Qt::PenStyle entries
//--------------------------------------------------------------
void DataPropertyWidget::set_penStyle_map_()
{
  penStyleContainer_.push_back("line");
  penStyleContainer_.push_back("circles");
  penStyleContainer_.push_back("points");
}



/***************************************************************
 *
 *
 * member functions for HistogramPropertyWidget
 *
 *
 ****************************************************************/

/*****************************************************************
 *
 * public member functions
 * 
 *****************************************************************/

//----------------------------------------------------------------
// constructor
//----------------------------------------------------------------
HistogramPropertyWidget::HistogramPropertyWidget(QWidget* myParent, 
    const QString& histogramName, DataObjectPtr theData, 
    PlotPropertiesPtr plotProperties, bool isNormalized)
  :
    BasePropertyWidget(myParent),
    parent_(myParent),
    numBins_(0),
    normalized_(isNormalized),
    histogramName_(histogramName),
    plotProperties_(plotProperties),
    data_(theData)
{
  status_ = SUCCESSFULLY_CONSTRUCTED;
}


//----------------------------------------------------------------
// main initialization function
//----------------------------------------------------------------
bool HistogramPropertyWidget::Init(int numBins, 
    const QString& dataName)
{
  if ( status_ != SUCCESSFULLY_CONSTRUCTED )
  {
    return false;
  }

  /* initialize data members */
  numBins_ = numBins;
  dataName_ = dataName;

  generate_main_layout_();
  generate_name_label_();
  generate_plot_property_selectors_();
  generate_bin_control_selectors_();
  generate_property_table_();
  populate_main_layout_();

  /* connection for re-drawing of the parent canvas */
  connect(this, SIGNAL(update_and_rescale_canvas()), parent_, 
      SLOT(redraw_and_rescale_canvas()));
  connect(this, SIGNAL(update_canvas()), parent_,
      SLOT(redraw_canvas()));
  connect(this, SIGNAL(recompute_histogram(const QString&,
      const QString&, int, bool)), parent_, 
      SLOT(update_histogram(const QString&, const QString&, 
      int, bool)));
  connect(this, SIGNAL(color_changed(const QString&, const QColor&)), 
      this, SIGNAL(update_canvas()));
  connect(this, SIGNAL(color_changed(const QString&, const QColor&)), 
      parent_, SIGNAL(graph_color_changed(const QString&, 
      const QColor&)));

  return true;
}


//----------------------------------------------------------------
// return name of histogram object
//----------------------------------------------------------------
const QString& HistogramPropertyWidget::get_name() const
{
  return histogramName_;
}


//----------------------------------------------------------------
// update the currently used data set
//----------------------------------------------------------------
void HistogramPropertyWidget::update_widget(DataObjectPtr newData)
{
  /* install new data */
  data_ = newData;

  /* recompute property table */
  populate_property_table_();
}


/*****************************************************************
 *
 * private slots 
 *
 *****************************************************************/

//----------------------------------------------------------------
// change the line width to the selected one in the 
// plotProperties container
//----------------------------------------------------------------
void HistogramPropertyWidget::change_line_width_(int newWidth)
{
  plotProperties_->set_line_width(newWidth);
  emit update_canvas();
}


//----------------------------------------------------------------
// open up a QColorDialog and change color to the requested
// value
//----------------------------------------------------------------
void HistogramPropertyWidget::change_color_()
{
  QColor aColor = QColorDialog::getColor();

  if ( aColor.isValid() && aColor != plotProperties_->get_color() )
  {
    /* update the properties and widgets */
    plotProperties_->set_color(aColor);
    set_widget_back_color_(plotColorChooser_, aColor);

    emit color_changed(histogramName_,aColor);
  }
}


//----------------------------------------------------------------
// tell main window to recompute the present histogram based 
// on the given number of bins
//----------------------------------------------------------------
void HistogramPropertyWidget::number_of_bins_changed_()
{
  int newNumOfBins = numBinsSelector_->value();
  normalized_ = normalizeSelector_->isChecked();
  emit recompute_histogram(dataName_, histogramName_, 
      newNumOfBins, normalized_);
}

  
  
/******************************************************************
 *
 * private member functions
 *
 ******************************************************************/

//-----------------------------------------------------------------
// generate main layout for current tab
//-----------------------------------------------------------------
void HistogramPropertyWidget::generate_main_layout_()
{
  mainLayout_ = new QVBoxLayout(this);
  mainLayout_->setContentsMargins(2,2,2,2);
}


//------------------------------------------------------------------
// generate widget to display name of data set whose histogram is
// being plotted
//------------------------------------------------------------------
void HistogramPropertyWidget::generate_name_label_()
{
  QLabel* titleLabel = new QLabel(tr("histogram of data set"));
  QLineEdit* nameLabel = new QLineEdit(strip_path(dataName_));
  nameLabel->setReadOnly(true);
  nameLabel->setCursorPosition(0);
  QVBoxLayout* nameLabelLayout = new QVBoxLayout;
  nameLabelLayout->addWidget(titleLabel);
  nameLabelLayout->addWidget(nameLabel);

  nameLabelGrouper_ = new QGroupBox;
  nameLabelGrouper_->setLayout(nameLabelLayout);
  nameLabelGrouper_->setAutoFillBackground(true);
  nameLabelGrouper_->setBackgroundRole(QPalette::Highlight);
  nameLabelGrouper_->setContentsMargins(2,2,2,2);
}


//------------------------------------------------------------------
// generate widget to display important data properties
//------------------------------------------------------------------
void HistogramPropertyWidget::generate_property_table_()
{
  propertyTable_ = new QTableWidget(NUM_ROWS_, NUM_COLUMNS_);
  propertyTable_->setMinimumHeight(PROPERTY_TABLE_HEIGHT_);
  propertyTable_->setMaximumHeight(PROPERTY_TABLE_HEIGHT_);

  /** items should be read-only and not selectable */
  propertyTable_->setSelectionMode(QAbstractItemView::NoSelection);
  propertyTable_->setEditTriggers(QAbstractItemView::NoEditTriggers);

  QStringList headerNames;
  headerNames << "value";

  QStringList propertyNames;
  propertyNames << "min" << "max" << "mean" 
    << "median" << "std. dev";

  propertyTable_->setHorizontalHeaderLabels(headerNames);
  propertyTable_->setVerticalHeaderLabels(propertyNames);

  /* size rows and columns */
  for ( int column = 0; column < headerNames.size(); ++column)
  {
    propertyTable_->setColumnWidth(column, COLUMN_WIDTH_);
  }

  for ( int row = 0; row < propertyNames.size(); ++row)
  {
    propertyTable_->setRowHeight(row, ROW_HEIGHT_);
  }

  minItem_ = new QTableWidgetItem;
  minItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(0, 0, minItem_);
  
  maxItem_ = new QTableWidgetItem;
  maxItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(1, 0, maxItem_);
 
  meanItem_ = new QTableWidgetItem;   
  meanItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(2, 0, meanItem_);
 
  medianItem_ = new QTableWidgetItem;   
  medianItem_->setTextAlignment(Qt::AlignRight);
  propertyTable_->setItem(3, 0, medianItem_);
 
  /* populate it with contents */
  populate_property_table_();
}


//------------------------------------------------------------------
// populates the property table with the actual data values
//------------------------------------------------------------------
void HistogramPropertyWidget::populate_property_table_()
{
  const vector<double>& histXData = data_->get_x_data();
  const vector<double>& histYData = data_->get_y_data();
  
  vector<double> binHeights;
  vector<double> binLocations;
  extract_bin_locations(histXData, binLocations);
  extract_bin_heights(histYData, binHeights);
  int  numElements = 
    extract_total_bin_element_count(normalized_, histYData);

  double min = 0.0;
  histogram_min(binHeights, min);
  minItem_->setText(pretty_format_double(min)); 
  
  double max = 0.0;
  histogram_max(binHeights, max);
  maxItem_->setText(pretty_format_double(max)); 

  double mean = 0.0;
  histogram_mean(binLocations, binHeights, numElements, mean);
  meanItem_->setText(pretty_format_double(mean)); 

  double median = 0.0;
  histogram_median(binLocations, binHeights, numElements, median);
  medianItem_->setText(pretty_format_double(median)); 
}


//------------------------------------------------------------------
// generate widget to display name of data set whose histogram is
// being plotted
//------------------------------------------------------------------
void HistogramPropertyWidget::generate_plot_property_selectors_()
{
  plotPropertiesLayout_ = new QGridLayout;
  plotPropertiesLayout_->setVerticalSpacing(1);

  /* add labels for widget */
  QLabel* colorLabel = new QLabel(tr("color"));
  QLabel* lineWidthLabel = new QLabel(tr("line width"));
  plotPropertiesLayout_->addWidget(colorLabel,0,1,Qt::AlignJustify);
  plotPropertiesLayout_->addWidget(
      lineWidthLabel,0,2,Qt::AlignJustify);

  /* add color selector */
  plotColorChooser_ = new QPushButton;
  set_widget_back_color_(plotColorChooser_, 
      plotProperties_->get_color());
  connect(plotColorChooser_,SIGNAL(clicked()),this,
    SLOT(change_color_()));
  plotPropertiesLayout_->addWidget(plotColorChooser_,1,1);

  /* add line width selector; 
   * for now, enable a range from 1 (default) to 10 */
  QComboBox* lineWidthChooser = new QComboBox;
  for(int lineWidth=1; lineWidth <= 10; ++lineWidth)
  {
    lineWidthChooser->addItem(QString::number(lineWidth));
  }

  plotPropertiesLayout_->addWidget(lineWidthChooser,1,2); 
  connect(lineWidthChooser,SIGNAL(currentIndexChanged(int)),
      this,SLOT(change_line_width_(int)));
}


//------------------------------------------------------------------
// generate widget with controls for the number of bins
//------------------------------------------------------------------
void HistogramPropertyWidget::generate_bin_control_selectors_()
{
  binControlLayout_ = new QGridLayout;
  binControlLayout_->setVerticalSpacing(1);

  /* add labels for widget */
  QLabel* spinBoxLabel = new QLabel(tr("number of bins"));
  binControlLayout_->addWidget(spinBoxLabel,0,1,Qt::AlignJustify);

  /* add bin selection widgets */
  numBinsSelector_ = new QSpinBox(this);
  numBinsSelector_->setRange(1,NUM_MAX_HISTOGRAM_BINS);
  numBinsSelector_->setValue(numBins_);
  binControlLayout_->addWidget(numBinsSelector_,1,1);

  QPushButton* recomputeHistogramButton = 
    new QPushButton(tr("recompute"));
  binControlLayout_->addWidget(recomputeHistogramButton,1,2);

  /* add normalization button */
  normalizeSelector_ = new QCheckBox(tr("normalize")); 
  normalizeSelector_->setChecked(normalized_);
  binControlLayout_->addWidget(normalizeSelector_,2,1);

  connect(recomputeHistogramButton,SIGNAL(clicked()),this,
      SLOT(number_of_bins_changed_()));
}




//-----------------------------------------------------------------
// populate main layout with all sub widgets 
//-----------------------------------------------------------------
void HistogramPropertyWidget::populate_main_layout_()
{
  mainLayout_->addWidget(nameLabelGrouper_);
  mainLayout_->addWidget(propertyTable_);
  mainLayout_->addLayout(plotPropertiesLayout_);
  mainLayout_->addLayout(binControlLayout_);
  mainLayout_->addStretch(1);
}




/*****************************************************************
 *
 *
 * definition of helper functions used by all property widgets
 *
 *
 ******************************************************************/

//-----------------------------------------------------------------
// set foreground (text) color of widgets
//-----------------------------------------------------------------
void set_widget_fore_color_(QWidget* aWidget, QColor newColor)
{
  QPalette widgetColor = QPalette();

  /* NOTE: the following fudges the text to always be
   * newColor; this should be easier! */
  widgetColor.setColor(QPalette::Active, QPalette::WindowText, 
      newColor);
  widgetColor.setColor(QPalette::Inactive, QPalette::WindowText, 
      newColor);
  aWidget->setPalette(widgetColor);
}


//-----------------------------------------------------------------
// set background color of widgets
//-----------------------------------------------------------------
void set_widget_back_color_(QWidget* aWidget, QColor newColor)
{
  QPalette widgetColor = QPalette(newColor);
  aWidget->setPalette(widgetColor);
}


